import 'dart:async';
import 'dart:convert';

import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';

/// Result
/// flutter 中解析json需要有固定的结构, 此类表示对原生端请求的结果
class Result {
  static final int codeForbidden = 403;
  final bool success;
  final String message;
  int code;
  dynamic data;

  Result(this.success, [this.message = '']);

  String toJSONString() => json.encode(this);

  static Result fromJSONString(String jsonString) {
    var map = json.decode(jsonString);
    var success = map['success'];
    var code = map['code'];
    var message = map['message'];
    var data = map['data'];
    return Result(success, message)
      ..code = code
      ..data = data;
  }

  static Result fromJson(Map<String, dynamic> map) {
    var success = map['success'];
    var code = map['code'];
    var message = map['message'];
    var data = map['data'];
    return Result(success, message)
      ..code = code
      ..data = data;
  }

  ///  json.encode(obj)调用时的callback方法
  Map<String, dynamic> toJson() {
    Map<String, dynamic> rtn = {};
    rtn['success'] = success ?? false;
    rtn['message'] = message;
    rtn['code'] = code;
    rtn['data'] = data;
    return rtn;
  }

  @override
  String toString() {
    return 'Result: success=$success, code=$code, message=$message';
  }
}

/// ~Result

const String _CHANNEL_FROM_FLUTTER = 'site.thatime.fnp/flutter_to_native';
const String _CHANNEL_TO_FLUTTER = 'site.thatime.fnp/native_to_flutter';

typedef NativeEventCallback = void Function(String event, [dynamic data]);

class Fnp {
  static const MethodChannel _methodChannel =
      const MethodChannel(_CHANNEL_FROM_FLUTTER);
  static const EventChannel _eventChannel =
      const EventChannel(_CHANNEL_TO_FLUTTER);
  static StreamSubscription _subscription;

  Fnp._();

  static Future<String> get platformVersion async {
    final String version =
        await _methodChannel.invokeMethod('getPlatformVersion');
    return version;
  }

  static Future<Result> execNativeMethodResult(String name,
      [dynamic arguments]) async {
    Future<Result> future;
    try {
      // 在通道上调用原生方法
      final String message = await _methodChannel.invokeMethod(name, arguments);
      Result result = Result.fromJSONString(message);
      future = Future.value(result);
    } on PlatformException catch (e) {
      if (e.details is int) {
        future = Future.value(Result(false, e.message)..code = e.details);
      } else {
        future = Future.value(Result(false, e.message));
      }
    } on MissingPluginException catch (e) {
      debugPrint("execNativeMethod Failed: ${e.message}");
      future = Future.value(Result(false, e.message));
    }
    return future;
  }

  static void subscribeNativeEvent(NativeEventCallback callback,
      {Function(dynamic err) onError}) {
    if (_subscription != null) {
      // 取消上一次订阅
//    _subscription.cancel();
      debugPrint('Native event already be subscribed!');
      return;
    }
    _subscription = _eventChannel.receiveBroadcastStream().listen((data) {
      Result result;
      if (data is String) {
        try {
          result = Result.fromJSONString(data);
          result == null
              ? callback(data)
              : callback(result.message, result.data);
        } catch (e) {
          print(e);
        }
      } else {
        callback(null, data);
      }
    }, onError: onError);
  }

  static void cancelSubscribed() {
    if (_subscription != null) {
      _subscription.cancel();
      _subscription = null;
    }
  }
}
